//! # 操作系统课程设计
//! 
//! > _多用户、多级目录结构文件系统的设计与实现_
//! > 
//! > _模拟实现 Unix 的文件系统_
//! 
//! 主要实现了文件模拟的一个精简版类 Unix 文件系统，实现了多目录与多用户，支持通过路径名查找文件，
//! 完成文件的创建、打开、写入、删除等操作。
//! 
//! 使用位示图法管理空闲的磁盘块。
//! 
//! ## 文件系统的结构
//! 
//! 实际使用的 Ext2 文件系统将磁盘由多个组块组成：
//! 
//! <img src="data:image/png;base64,
#![doc = include_str!("../img/ext2.b64")]
//! "/>
//! 
//! 其中 **引导块（BootBlock）** 位于磁盘分区开头 1024 字节，存储由 BIOS 自动读入的引导程序和数据
//! 这个块和 ext2 没有任何关系。
//! 
//! 分区剩余的部分被分为若干个组。每个组里均由一个 **super block** 块和一个 **Group Descriptors（组描述符）块**
//! 组成。组描述符里定义了诸如文件系统的静态结构，包括块的大小，总块数，每组内inode数，
//! 空闲块，索引结点数等全局信息。
//! 
//! Linux启动时 super block 0 的内容读入内存，某个组损坏可用来恢复。
//! 
//! 我们模拟的类 Unix 文件系统**剔除**了引导块和超级块，整个文件系统**仅由一个组块**构成：
//! 
//! <img src="data:image/png;base64,
#![doc = include_str!("../img/disk_struct.b64")]
//! "/>
//! 
//! 现在我们的文件系统结构如下：
//! 
//! - **组描述符 [`GroupDesc`](fs::GroupDesc)**
//! 
//!   存储在第一个磁盘块，这个数据结构存储了用户名和密码，磁盘布局，空闲索引结点（i结点）个数，空闲数据块的个数
//!   等信息。
//! 
//! - **数据块位图**
//! 
//!   存储在第二个磁盘块。
//! 
//!   这是ext2管理存储空间的方法，即位图法。每个位对应一个数据块，位值为 0 表示空闲，1 表示已经分配。
//!   数据块位图定义为一个块大小。于是，一个组中的数据块个数就决定了。假设块大小为 b 字节。可以区别的块数为 b*8 个。
//! 
//!   在我们模拟的文件系统里，将磁盘块的大小（[`fs::BLOCK_SIZE`]）为 512 字节，那么这个位图只能记录 512 * 8 = 4096
//!   个数据块的使用情况。 
//! 
//!   因此，我们模拟的磁盘（只有一个组块）数据区的大小也就确定为 4096 * 512 = 2097152 字节 = 2MB
//! 
//! - **i 结点（索引结点）位图**
//! 
//!   存储在磁盘上的第三块磁盘块。我们已经知道这个磁盘块最多能记录 4096 个 i 结点的使用情况，在我们的文件系统里，
//!   每个 i 结点（[`Inode`](fs::Inode)）占用 32 个字节，在磁盘上需要 4096 * 32 = 131072 字节的空间来存储 i 结点。
//! 
//!   同时也能算出需要给 i 结点分配 131072 / 512 = 256 个磁盘块
//! 
//! - **i 结点 [`Inode`](fs::Inode)**
//! 
//!   存储在第 4 个 到 第 4 + 256 个磁盘块里。
//! 
//!   也被称为索引结点，这个数据结构存储了关于文件的所有信息，如创建时间、存取权限、数据块索引、文件大小等信息。
//!   **但不存储文件名**，文件名被存储到目录项 [`DirEntry`](fs::DirEntry) 里，后者存储在数据块里。
//! 
//! - **数据块**
//! 
//!   存储在第 4 + 256 + 1 到 4 + 256 + 1 + 4096 个磁盘块上。
//! 
//!   这里是磁盘的最后一块区域，也是存储文件内容的地方，里面包含了文件的内容，文件夹（目录）的相关信息也会存储在数据块里。
//!   即使文件的大小小于数据块的大小，文件在也会占用一整块数据块。
//! 
//! # 主要的数据结构
//! 
//! ## 组描述符 - [GroupDest](fs::GroupDesc)
//!  
//! 定义了块位图的块号，索引结点位图的块号、索引结点表的起始块号，本组空闲块的个数等组内信息。
//! 文件系统根据这些信息来查找数据块位图，索引结点位图，索引结点表的位置。
//! 
//! ```ignore
//! // /src/fs/core/fs.rs
//! pub struct GroupDesc {
//!    /// 卷名
//!    pub volume_name: [u8; 16],
//!    /// 块位图所在的块号
//!    pub block_bitmap: u16,
//!    /// 索引结点位图的块号
//!    pub inode_bitmap: u16,
//!    /// 索引表的起始块号
//!    pub inode_table: u16,
//!    /// 空闲块的个数
//!    pub free_blocks_count: u16,
//!    /// 空闲索引节点的个数    
//!    pub free_inodes_count: u16,
//!    /// 目录个数
//!    pub used_dirs_count: u16,
//!    /// 用户表
//!    pub users: [User; 10],
//!    pub users_len: u16,
//!}
//! ```
//! 
//! 组描述符的默认数据如下，在格式化（[`Fs::format()`](fs::Fs::format())）时会新建一个组描述符，然后将其
//! 写入第一块磁盘块：
//! 
//! ```ignore
//! // /src/fs/core/fs.rs
//! impl GroupDesc {
//!     pub(in crate::fs) fn new() -> Self {
//!         let mut users = [User::default(); 10];
//!     
//!         // 默认用户
//!         users[0] = User {
//!             name: "root".into_array().unwrap(),
//!             password: "123".into_array().unwrap(),
//!         };
//!     
//!         Self {
//!             volume_name: "Ext2Disk".into_array().unwrap(),
//!             block_bitmap: 1,
//!             inode_bitmap: 2,
//!             inode_table: 3,
//!             free_blocks_count: DATA_BLOCKS as u16,
//!             free_inodes_count: DATA_BLOCKS as u16,
//!             used_dirs_count: 0,
//!             users_len: 1,
//!             users,
//!         }
//!     }
//! }
//! ```
//!
//! ## 索引结点 - [`Inode`](fs::Inode)
//! 
//! 每个索引结点数据结构大小为 32 字节。每个索引结点即对应一个文件或是目录。是对其除文件名（目录名）以外的所有属性的描述。
//! 例如：文件类型，文件创建时间，访问时间，修改时间，文件所占数据块的个数，指向数据块的指针。
//! 
//! ```ignore
//! pub struct Inode {
//!     /// 文件权限
//!     pub i_mode: FileMode,
//!     /// 文件数据块个数
//!     pub i_blocks: u16,
//!     /// 文件大小
//!     pub i_size: u32,
//!     /// 创建时间
//!     pub i_ctime: u32,
//!     /// 修改时间
//!     pub i_mtime: u32,
//!     /// 指向数据块的指针数组（使用二级索引）
//!     pub(in crate::fs) i_block: [u16; 8],
//! }
//! ```
//! 数据块指针 [`i_block`](fs::Inode::i_block) 是由 8个元组的数据组成：
//! 
//! - i_block[0]~i_block[5] 直接指向数据块（直接索引）
//! - i_block[6] 是一个1级子索引。指向的不是数据块，而是存放数据块指针的块。
//! - 类似的，i_block[7] 是一个2级子索引。
//! 
//! 我们这里 i_block 存放的是数据块的块号。
//! 
//! 假设文件块大小为 b 字节，标识数据块的地址大小为 4 字节，那么每个数据块可以存储 b / 4
//! 个地址，也就是说每个数据块可以索引 b / 4 个其他的数据块
//! 
//! 1. 则当文件长度小于 b*6 时，只要用 i_block[0]~i_block[5] 来指向其数据块
//! 2. 当文件长度在 b*6 到 b*(6 + b / 4) 时，使用 i_block[6] 指向的数据块作为索引
//! 3. 当文件长度在 b*(6+b/4) 到 b*(6 + b/4 + (b/4)^2 时，使用 i_block[7] 指向的两级索引
//! 
//! <img src="data:image/png;base64,
#![doc = include_str!("../img/i_block.b64")]
//! "/>
//! 
//! <img src="data:image/png;base64,
#![doc = include_str!("../img/i_block_addr.b64")]
//! "/>
//! 
//! ### 文件和目录：
//! 
//! 目录是一种特殊的文件。其数据块中的内容即目录表：所包含的文件（当然也包括子目录）的文件名，
//! 指向文件的索引节点号，文件类型。 
//! 
//! ```ignore
//! // src/fs/core/fs.rs
//! pub struct DirEntry {
//!    /// 索引节点号
//!    pub i_node: u16,
//!    /// 目录项长度
//!    pub rec_len: u16,
//!    /// 文件名长度
//!    pub name_len: u8,
//!    /// 文件类型
//!    pub file_type: u8,
//!    /// 文件名
//!    pub name: [u8; 16],
//!}
//! ```
//! 
//! 目录文件的数据块：
//! 
//! <img src="data:image/png;base64,
#![doc = include_str!("../img/dir_entry.b64")]
//! "/>
//! 
//! inode 标识的是目录项下文件的 i节点，当文件被删除时，inode
//! 设置为 0
//! 
#[doc = "fs::Inode"]
pub mod shell;
pub mod utils;
pub mod fs;